package com.cucn.ingmall.ware.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cucn.common.constant.order.OrderStatusEnum;
import com.cucn.common.constant.ware.WareConstant;
import com.cucn.common.exception.NoStockException;
import com.cucn.common.to.SkuHasStockVO;
import com.cucn.common.to.mq.OrderTO;
import com.cucn.common.to.mq.StockDetailTO;
import com.cucn.common.to.mq.StockLockedTO;
import com.cucn.common.utils.PageUtils;
import com.cucn.common.utils.Query;
import com.cucn.common.utils.R;
import com.cucn.ingmall.ware.dao.WareSkuDao;
import com.cucn.ingmall.ware.dto.OrderDTO;
import com.cucn.ingmall.ware.entity.WareOrderTaskDetailEntity;
import com.cucn.ingmall.ware.entity.WareOrderTaskEntity;
import com.cucn.ingmall.ware.entity.WareSkuEntity;
import com.cucn.ingmall.ware.feign.OrderFeignService;
import com.cucn.ingmall.ware.feign.ProductFeignService;
import com.cucn.ingmall.ware.service.WareOrderTaskDetailService;
import com.cucn.ingmall.ware.service.WareOrderTaskService;
import com.cucn.ingmall.ware.service.WareSkuService;
import com.cucn.ingmall.ware.vo.OrderItemVO;
import com.cucn.ingmall.ware.vo.WareSkuLockDTO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Slf4j
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private ProductFeignService productFeignService;

    @Resource
    private WareOrderTaskDetailService wareOrderTaskDetailService;
    @Resource
    private WareOrderTaskService wareOrderTaskService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private OrderFeignService orderFeignService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        String skuId = (String) params.get("skuId");
        if (StringUtils.isNotEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }

        String wareId = (String) params.get("wareId");
        if (StringUtils.isNotEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    /**
     * 将采购人员采购的商品入库
     *
     * @param skuId  skuId
     * @param wareId 仓库id
     * @param skuNum sku的数量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //判断是更新还是新增操作
        List<WareSkuEntity> skuEntityList = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (skuEntityList == null || skuEntityList.size() == 0) {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setStockLocked(0);
            //远程查询,如果失败整个事务不需要回滚
            //KouTODO:2021/12/7 11:25 Kou 还有什么办法出异常之后不回滚？
            try {
                R r = productFeignService.info(skuId);
                Map<String, Object> skuInfo = (Map<String, Object>) r.get("skuInfo");
                if (r.getCode() == 0) {
                    wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));
                }
            } catch (Exception ignored) {

            }
            wareSkuDao.insert(wareSkuEntity);
        } else {
            wareSkuDao.addStock(skuId, wareId, skuNum);
        }
    }

    /**
     * 判断sku是否有库存
     *
     * @param skuIds skuId集合
     * @return sku库存vo对象集合
     */
    @Override
    public List<SkuHasStockVO> getSkusHasStock(List<Long> skuIds) {
        List<SkuHasStockVO> skuHasStockVOS = skuIds.stream().map(skuId -> {
            SkuHasStockVO vo = new SkuHasStockVO();
            //查询是否有库存
            Integer stockCount = baseMapper.getSkuStock(skuId);
            vo.setSkuId(skuId);
            vo.setHasStock(stockCount != null && stockCount > 0);

            return vo;
        }).collect(Collectors.toList());
        return skuHasStockVOS;
    }

    /**
     * 为某个订单锁定库存
     *
     * @param wareSkuLockDTO 需锁定的库存信息
     * @return 返回每个商品锁定库存成功与否的信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean orderLockSkuWare(WareSkuLockDTO wareSkuLockDTO) {

        log.info("开始锁定库存.....");

        /*
        保存库存工作单的详情,方便追溯
         */
        WareOrderTaskEntity wareOrderTask = new WareOrderTaskEntity();
        wareOrderTask.setCreateTime(new Date());
        wareOrderTask.setOrderSn(wareSkuLockDTO.getOrderSn());
        wareOrderTaskService.save(wareOrderTask);

        //1.先找到商品在哪个仓库有库存
        List<OrderItemVO> skuLockInfo = wareSkuLockDTO.getLocks();

        List<SkuWareHasStock> skuWareHasStockList = skuLockInfo.stream().map(skulockInfoItem -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            skuWareHasStock.setSkuId(skulockInfoItem.getId());
            skuWareHasStock.setLockNum(skulockInfoItem.getCount());
            //商品在哪个仓库有库存
            List<Long> wareIdList = wareSkuDao.selectWareSkuHasStock(skulockInfoItem.getId());
            skuWareHasStock.setWareId(wareIdList);

            return skuWareHasStock;
        }).collect(Collectors.toList());

        //2.锁定库存
        for (SkuWareHasStock wareHasStock : skuWareHasStockList) {
            boolean stockSuccess = false;
            Long skuId = wareHasStock.getSkuId();
            Integer lockNum = wareHasStock.getLockNum();

            List<Long> wareIdList = wareHasStock.getWareId();
            if (CollectionUtils.isEmpty(wareIdList)) {
                throw new NoStockException(skuId);
            }

            for (Long wareId : wareIdList) {
                Integer lockResult = wareSkuDao.lockStock(skuId, wareId, lockNum);
                if (lockResult == 1) {
                    stockSuccess = true;

                    //创建库存工作单详情
                    WareOrderTaskDetailEntity wareOrderTaskDetail = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetail.setTaskId(wareOrderTask.getId());
                    wareOrderTaskDetail.setWareId(wareId);
                    wareOrderTaskDetail.setSkuId(skuId);
                    wareOrderTaskDetail.setSkuNum(lockNum);
                    wareOrderTaskDetail.setLockStatus(WareConstant.LOCK_STATUS_SUCCESS);
                    wareOrderTaskDetailService.save(wareOrderTaskDetail);

                    StockLockedTO stockLockedTO = new StockLockedTO();
                    stockLockedTO.setTaskId(wareOrderTask.getId());
                    StockDetailTO stockDetailTO = new StockDetailTO();
                    BeanUtils.copyProperties(wareOrderTaskDetail, stockDetailTO);
                    //不能只发id,防止回滚之后找不到数据
                    stockLockedTO.setStockDetailTO(stockDetailTO);
                    rabbitTemplate.convertAndSend(WareConstant.STOCK_EVENT_EXCHANGE, WareConstant.STOCK_LOCKED_BINDING_ROUTINGKEY, stockLockedTO);

                    break;
                } else {
                    //当前仓库没锁住,尝试下个仓库
                }
            }

            //如果都没锁住,锁定库存操作则失败
            if (stockSuccess == false) {
                throw new NoStockException(skuId);
            }
        }

        //锁定库存成功
        return true;
    }

    /**
     * 库存过期消息解锁库存方法
     *
     * @param stockLockedTO 给mq传输的库存锁定信息的TO
     */
    @Override
    public void unLockStock(StockLockedTO stockLockedTO) {

        StockDetailTO stockDetailTO = stockLockedTO.getStockDetailTO();
        Long detailTOId = stockDetailTO.getId();

        WareOrderTaskDetailEntity detailServiceById = wareOrderTaskDetailService.getById(detailTOId);
        log.info("库存工作单详情:{}", detailServiceById);
        if (ObjectUtil.isNotEmpty(detailServiceById)) {
            //库存工作单有数据,解锁
            //获取本次锁库存操作的订单号
            String orderSn = wareOrderTaskService.getById(stockLockedTO.getTaskId()).getOrderSn();

            log.info("锁定库存操作的订单号:{}", orderSn);

            R rOrderStatus = orderFeignService.getOrderStatus(orderSn);

            if (rOrderStatus.getCode() == 0) {
                //订单状态返回成功
                OrderDTO orderDTO = rOrderStatus.getData(new TypeReference<OrderDTO>() {
                });
                /*
                订单不存在,解锁
                订单被取消,解锁库存
                 */
                if (ObjectUtil.isEmpty(orderDTO) || orderDTO.getStatus().equals(OrderStatusEnum.CANCLED.getCode())) {
                    log.info("订单被取消,解锁库存");
                    //订单被取消,解锁库存
                    Long skuId = stockDetailTO.getSkuId();
                    Long wareId = stockDetailTO.getWareId();
                    Integer skuNum = stockDetailTO.getSkuNum();
                    //当前库存工作单已锁定才能解锁
                    if (detailServiceById.getLockStatus().equals(WareConstant.LOCK_STATUS_SUCCESS)) {
                        log.info("开始解锁库存.....skuid:{},wareid:{},skuNum:{},detailId:{}", skuId, wareId, skuNum, detailTOId);
                        unLockStock(skuId, wareId, skuNum, detailTOId);
                    }
                }
            } else {
                throw new RuntimeException("远程服务失败");
            }
        }
        //无需解锁
    }

    /**
     * 订单关单消息解锁库存方法
     * 防止订单服务卡顿,导致订单状态消息一直改不了,库存消息优先到期。查订单状态为新建状态，什么都不做就走了
     * 导致卡顿的订单,永远不能解锁库存
     *
     * @param orderTO
     */
    @Override
    @Transactional
    public void unLockStock(OrderTO orderTO) {
        String orderSn = orderTO.getOrderSn();

        //解锁库存
        //查询一下最新的库存解锁状态,防止重复解锁库存
        WareOrderTaskEntity wareOrderTask = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        List<WareOrderTaskDetailEntity> detailList = wareOrderTaskDetailService
                .list(new QueryWrapper<WareOrderTaskDetailEntity>()
                        .eq("task_id", wareOrderTask.getId())
                        .eq("lock_status", WareConstant.LOCK_STATUS_SUCCESS));

        for (WareOrderTaskDetailEntity detail : detailList) {
            unLockStock(detail.getSkuId(), detail.getWareId(), detail.getSkuNum(), detail.getId());
        }
    }

    /**
     * 解锁库存方法
     *
     * @param skuId        商品id
     * @param wareId       仓库id
     * @param num          锁定的数量
     * @param taskDetailId 库存工作详情单id
     */
    @Transactional
    void unLockStock(Long skuId, Long wareId, Integer num, Long taskDetailId) {
        Integer unLockStockResult = wareSkuDao.unLockStock(skuId, wareId, num);
        log.info("库存解锁成功.....更新库存工作单详情的解锁状态......");
        if (unLockStockResult == 1) {
            //更新库存工作单详情的解锁状态
            WareOrderTaskDetailEntity detail = new WareOrderTaskDetailEntity();
            detail.setId(taskDetailId);
            detail.setLockStatus(2);
            wareOrderTaskDetailService.updateById(detail);
        }
    }

    /**
     * sku与仓库对应关系
     */
    @Data
    private static class SkuWareHasStock {
        /**
         * 商品id
         */
        private Long skuId;
        /**
         * 商品对应的仓库id
         */
        private List<Long> wareId;
        /**
         * 将要锁定的库存数量
         */
        private Integer lockNum;
    }
}